
package com.trydofor.id.a9ic;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.concurrent.ThreadSafe;

import com.trydofor.id.core.A9CQ;
import com.trydofor.id.core.ApiExceptions;

/**
 * a thread-safe instance.
 * 
 * @author Shi Rongjiu (www.trydofor.com)
 * @since 0.9
 */
@ThreadSafe
public class A9icMixedMaker extends A9icAbstractMaker {
    
    private final ReentrantLock lock = new ReentrantLock();
    private final A9CQ<Long>    a9cq;
    
    /**
     * get a new thread-safe instance.
     * 
     * <b>NOTE</b>: ReentrantLock sync when the assigned a9cq is NOT atomic.
     * 
     * @param worker the worker
     * @param a9cq the a9cq assigned outer.
     */
    public A9icMixedMaker(int worker, A9CQ<Long> a9cq) {
        super(worker);
        this.a9cq = a9cq;
    }
    
    /**
     * protected by ReentrantLock if NOT atomic.
     * 
     * {@inheritDoc}
     * 
     * @throws IllegalArgumentException if assigned a9cq out of range of a9ic.
     * @see A9IC
     */
    public A9IC make() {
        A9IC r = null;
        try {
            r = make(0, null);
        }
        catch (InterruptedException e) {
            ApiExceptions.throwFakeException(e);
        }
        catch (TimeoutException e) {
            ApiExceptions.throwFakeException(e);
        }
        return r;
    }
    
    /**
     * protected by ReentrantLock if NOT atomic.
     * 
     * {@inheritDoc}
     * 
     * @throws IllegalArgumentException if assigned a9cq out of range of a9ic.
     * @see A9IC
     */
    public A9IC make(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
        
        final long startedNano = System.nanoTime();
        final Thread curThread = Thread.currentThread();
        final boolean needTime = (timeout > 0 && unit != null);
        
        final Long sq;
        if (a9cq.isAtomic()) {
            sq = a9cq.next();
        }
        else {
            lock.lock();
            try {
                // pre-check
                if (needTime) {
                    ApiExceptions.checkThreadInterrupted(curThread);
                    ApiExceptions.checkTimeoutNano(startedNano, timeout, unit);
                }
                
                sq = a9cq.next();
            }
            finally {
                lock.unlock();
            }
        }
        
        // post-check
        if (needTime) {
            ApiExceptions.checkThreadInterrupted(curThread);
            ApiExceptions.checkTimeoutNano(startedNano, timeout, unit);
        }
        
        return sq == null ? null : new A9IC(worker, sq);
    }
    
    @Override
    public String toString() {
        return getClass() + ", worker=" + worker + ", a9cq=" + a9cq;
    }
}
